namespace DCHealthCheck.DomainModel {

    #region Usings
    using DCHealthCheck.Common;
    using System;
    using System.Collections.Generic;
    using System.Collections.ObjectModel;
    using System.ComponentModel.DataAnnotations;
    using System.ComponentModel.DataAnnotations.Schema;
    using System.Diagnostics;
    using System.Linq;
    using System.Text; 
    #endregion

    [Table("SystemInfo")]
    public class SystemInfo : ISystemInfo, IObjectWithState, IValidatableObject {

        #region Members
        #region Max length fields
        public static readonly int IPAddressMaxLength = 255;
        public static readonly int IPAddressesMaxLength = 255;
        public static readonly int ManufacturerMaxLength = 255;
        public static readonly int ModelMaxLength = 255;
        public static readonly int SerialNumberMaxLength = 255;
        public static readonly int TimeDiffMaxLength = 255;
        public static readonly int DNSResponseMaxLength = 255;
        public static readonly int DNSListenAddressesMaxLength = 255;
        public static readonly int DNSPublishAddressesMaxLength = 255;
        public static readonly int DCFlagsMaxLength = 255;
        public static readonly int SysvolReplicationTypeMaxLength = 255;
        public static readonly int SysvolMigrationStateMaxLength = 255;
        #endregion

        public virtual int CDriveFreeMB { get; set; }

        public virtual int CDriveTotalMB { get; set; }

        [DatabaseGenerated(DatabaseGeneratedOption.Computed)]
        public virtual DateTime CheckDateUTC { get; set; }

        public virtual DateTime CheckDateTimeUTC { get; set; }

        public virtual int CPUCount { get; set; }

        public static readonly string CSVHeader =
            "DCName,Domain,ADSite,OSVersion,HealthCheckDate," +
            "PingLossPct,PingRttMs,PingTest,DNSTest,LdapTest,LdapSSLTest," +
            "GCTest,GCSSLTest,StrictReplicationTest,SysvolTest,TimeServerTest,TimeSyncTest,ErrorMessage," +
            "Manufacturer,SerialNumber,CDriveFreeMB,CDriveTotalMB,CPUCount,LogicalCPUCount,FreeMemoryMB,TotalMemoryMB," +
            "IPAddress,IPAddresses,TcpConnections,ProcessCount,TotalHandles,TotalThreads,TotalLogons," +
            "DCTimeUTC,DST,OSInstallDate,StartupDateTimeUTC,TimeDiff," +
            "DNSResponse,DNSListenAddresses,DNSPublishAddresses,DNSQueryReceived,DNSResponseSent,DNSRecursiveQueries,DNSScavengingInterval," +
            "DCFlags,HighestCommittedUSN,IsGlobalCatalog,IsInfrastructureManager,IsISTG,IsPDCEmulator,IsRIDManager,NeighborCount," +
            "NTDSDitSizeMB,PendingReplicationCount,RepInMB,RepOutMB,SysvolMigrationState,SysvolReplicationType";

        public virtual DCFlags DCFlags { get; set; }

        public virtual DateTime DCTime { get; set; }

        public virtual DateTime DCTimeUTC { get; set; }

        public virtual string DNSListenAddresses { get; set; }

        public virtual string DNSPublishAddresses { get; set; }

        public virtual long DNSQueryReceived { get; set; }

        public virtual string DNSResponse { get; set; }

        public virtual long DNSResponseSent { get; set; }

        public virtual long DNSRecursiveQueries { get; set; }

        public virtual int DNSScavengingInterval { get; set; }

        public virtual bool DST { get; set; }

        public virtual string ErrorMessage { get; set; }

        public virtual int FreeMemoryMB { get; set; }

        public virtual byte[] GpRsopHtml { get; set; }

        public virtual byte[] GpRsopXml { get; set; }

        public virtual long HighestCommittedUSN { get; set; }

        [Key]
        public virtual Guid Id { get; set; }

        public virtual string IPAddress { get; set; }

        public virtual string IPAddresses { get; set; }

        public virtual bool IsGlobalCatalog { get; set; }

        public virtual bool IsInfrastructureManager { get; set; }

        public virtual bool IsISTG { get; set; }

        public virtual bool IsPDCEmulator { get; set; }

        public virtual bool IsRIDManager { get; set; }

        public virtual int LogicalCPUCount { get; set; }

        public virtual string Manufacturer { get; set; }

        public virtual string Model { get; set; }

        public virtual int NeighborCount { get; set; }

        public virtual int NTDSDitSizeMB { get; set; }

        public virtual DateTime OSInstallDate { get; set; }

        public virtual int PendingReplicationCount { get; set; }

        public virtual int PingRttMs { get; set; }

        public virtual int PingLossPct { get; set; }

        public virtual int ProcessCount { get; set; }

        public virtual int RepInMB { get; set; }

        public virtual int RepOutMB { get; set; }

        [Timestamp]
        public virtual byte[] RowVersion { get; set; }

        public virtual string SerialNumber { get; set; }

        public virtual DateTime StartupDateTimeUTC { get; set; }

        public virtual SysvolMigrationState SysvolMigrationState { get; set; }

        public virtual SysvolReplicationType SysvolReplicationType { get; set; }

        public virtual int TcpConnections { get; set; }

        public virtual string TimeDiff { get; set; }

        public virtual int TotalHandles { get; set; }

        public virtual int TotalLogons { get; set; }

        public virtual int TotalMemoryMB { get; set; }

        public virtual int TotalThreads { get; set; }

        public virtual int UTCOffset { get; set; }

        public virtual int UptimeDays { get; set; }

        #region TestResult properties
        public virtual TestResult DNSTest { get; set; }
        public virtual TestResult GCBindTest { get; set; }
        public virtual TestResult GCSSLBindTest { get; set; }
        public virtual TestResult LdapBindTest { get; set; }
        public virtual TestResult LdapSSLBindTest { get; set; }
        public virtual TestResult PingTest { get; set; }
        public virtual TestResult StrictReplicationTest { get; set; }
        public virtual TestResult SysvolTest { get; set; }
        public virtual TestResult TimeServerTest { get; set; }
        public virtual TestResult TimeSyncTest { get; set; }
        #endregion

        #region Related Entities
        public virtual DomainController DomainController {
            get { return domainController; }
            set {
                domainController = value;
                if (domainController != null) {
                    if (this.DomainControllerId != domainController.Id) {
                        this.DomainControllerId = domainController.Id;
                    }
                }
            }
        }
        private DomainController domainController;

        [ForeignKey("DomainController")]
        public virtual Guid DomainControllerId {
            get {
                if (this.DomainController != null) return this.DomainController.Id;
                return domainControllerId;
            }
            set { domainControllerId = value; }
        }
        private Guid domainControllerId;

        public virtual GroupPolicySysvolInfo GroupPolicySysvolInfo { get; set; }

        public virtual HealthCheckSummary HealthCheckSummary {
            get { return healthCheckSummary; }
            set {
                healthCheckSummary = value;
                if (healthCheckSummary != null) {
                    if (this.HealthCheckSummaryId != healthCheckSummary.Id) {
                        this.HealthCheckSummaryId = healthCheckSummary.Id;
                    }
                }
            }
        }
        private HealthCheckSummary healthCheckSummary;

        [ForeignKey("HealthCheckSummary")]
        public virtual int HealthCheckSummaryId {
            get {
                if (this.HealthCheckSummary != null) return this.HealthCheckSummary.Id;
                return healthCheckSummaryId;
            }
            set { healthCheckSummaryId = value; }
        }
        private int healthCheckSummaryId;

        public virtual ICollection<Certificate> Certificates {
            get {
                if (certificates == null) certificates = new List<Certificate>();
                return certificates;
            }
            set { certificates = value; }
        }
        private ICollection<Certificate> certificates;

        public virtual ICollection<InstalledProduct> InstalledProducts {
            get {
                if (installedProducts == null) installedProducts = new List<InstalledProduct>();
                return installedProducts;
            }
            set { installedProducts = value; }
        }
        private ICollection<InstalledProduct> installedProducts;

        public virtual ICollection<NetstatInfo> NetstatInfos {
            get {
                if (netstatInfos == null) netstatInfos = new List<NetstatInfo>();
                return netstatInfos;
            }
            set { netstatInfos = value; }
        }
        private ICollection<NetstatInfo> netstatInfos;

        public virtual ICollection<PendingReplication> PendingReplications {
            get {
                if (pendingReplications == null) pendingReplications = new List<PendingReplication>();
                return pendingReplications;
            }
            set { pendingReplications = value; }
        }
        private ICollection<PendingReplication> pendingReplications;

        public virtual ICollection<ProcessInfo> ProcessInfos {
            get {
                if (processInfos == null) processInfos = new List<ProcessInfo>();
                return processInfos;
            }
            set { processInfos = value; }
        }
        private ICollection<ProcessInfo> processInfos;

        public virtual ICollection<ReplicationNeighbor> ReplicationNeighbors {
            get {
                if (replicationNeighbors == null) replicationNeighbors = new List<ReplicationNeighbor>();
                return replicationNeighbors;
            }
            set { replicationNeighbors = value; }
        }
        private ICollection<ReplicationNeighbor> replicationNeighbors;

        public virtual ICollection<RoleFeature> RoleFeatures {
            get {
                if (roleFeatures == null) roleFeatures = new List<RoleFeature>();
                return roleFeatures;
            }
            set { roleFeatures = value; }
        }
        private ICollection<RoleFeature> roleFeatures;

        public virtual ICollection<ServiceInfo> ServiceInfos {
            get {
                if (serviceInfos == null) serviceInfos = new List<ServiceInfo>();
                return serviceInfos;
            }
            set { serviceInfos = value; }
        }
        private ICollection<ServiceInfo> serviceInfos;

        public virtual ICollection<TagObjectValue> TagObjectValues {
            get {
                if (tagObjectValues == null) tagObjectValues = new List<TagObjectValue>();
                return tagObjectValues;
            }
            set { tagObjectValues = value; }
        }
        private ICollection<TagObjectValue> tagObjectValues;

        public virtual TimeSyncInfo TimeSyncInfo { get; set; }
        #endregion

        #region NotMapped properties
        [NotMapped]
        public DetachedState DetachedState { get; set; }

        [NotMapped]
        public Object EntityKey { get { return this.Id; } }

        [NotMapped]
        public StringBuilder ErrorMessageBuilder { get; set; }

        [NotMapped]
        public Dictionary<string, object> StartingOriginalValues { get; set; }

        [NotMapped]
        public string TimeDiffFriendly {
            get {
                timeDiffFriendly = this.TimeDiff;
                TimeSpan timeSpan = TimeSpan.Zero;
                if (!string.IsNullOrWhiteSpace(this.TimeDiff) && (TimeSpan.TryParse(this.TimeDiff, out timeSpan))) {
                    timeDiffFriendly = timeSpan.HMSFriendly();
                }

                return timeDiffFriendly;
            }
        }
        private string timeDiffFriendly;
        #endregion 
        #endregion

        #region Constructor
        public SystemInfo() {
            this.CDriveFreeMB = -1;
            this.CDriveTotalMB = -1;
            this.CPUCount = -1;
            this.DNSListenAddresses = string.Empty;
            this.DNSPublishAddresses = string.Empty;
            this.DNSQueryReceived = -1;
            this.DNSRecursiveQueries = -1;
            this.DNSResponse = string.Empty;
            this.DNSResponseSent = -1;
            this.DNSScavengingInterval = -1;
            this.ErrorMessage = string.Empty;
            this.FreeMemoryMB = -1;
            this.HighestCommittedUSN = -1;
            this.Id = Guid.NewGuid();
            this.IPAddress = string.Empty;
            this.IPAddresses = string.Empty;
            this.LogicalCPUCount = -1;
            this.Manufacturer = string.Empty;
            this.Model = string.Empty;
            this.NeighborCount = -1;
            this.NTDSDitSizeMB = -1;
            this.PendingReplicationCount = -1;
            this.PingLossPct = -1;
            this.PingRttMs = -1;
            this.ProcessCount = -1;
            this.RepInMB = -1;
            this.RepOutMB = -1;
            this.SerialNumber = string.Empty;
            this.SysvolMigrationState = Common.SysvolMigrationState.NotSpecified;
            this.SysvolReplicationType = Common.SysvolReplicationType.NotSpecified;
            this.TcpConnections = -1;
            this.TimeDiff = string.Empty;
            this.TotalHandles = -1;
            this.TotalLogons = -1;
            this.TotalMemoryMB = -1;
            this.TotalThreads = -1;
            this.UptimeDays = -1;
            this.UTCOffset = -1;

            this.ErrorMessageBuilder = new StringBuilder();
        } 
        #endregion

        #region Methods
        public string ToCSVString() {
            var info = new StringBuilder();

            #region DC Info
            info.AppendFormat("\"{0}\",", this.DomainController.SamAccountNameFriendly);
            info.AppendFormat("\"{0}\",", this.DomainController.NetbiosDomain);
            info.AppendFormat("\"{0}\",", this.DomainController.ADSite);
            info.AppendFormat("\"{0}\",", this.DomainController.OSVersion);

            info.AppendFormat("\"{0}\",", this.CheckDateTimeUTC.ToString("yyyy-MM-dd HH:mm"));
            #endregion
            #region Test Results
            info.AppendFormat("\"{0}\",", this.PingLossPct);
            info.AppendFormat("\"{0}\",", this.PingRttMs);
            info.AppendFormat("\"{0}\",", this.PingTest);
            info.AppendFormat("\"{0}\",", this.DNSTest);
            info.AppendFormat("\"{0}\",", this.LdapBindTest);
            info.AppendFormat("\"{0}\",", this.LdapSSLBindTest);
            info.AppendFormat("\"{0}\",", this.GCBindTest);
            info.AppendFormat("\"{0}\",", this.GCSSLBindTest);
            info.AppendFormat("\"{0}\",", this.StrictReplicationTest);
            info.AppendFormat("\"{0}\",", this.SysvolTest);
            info.AppendFormat("\"{0}\",", this.TimeServerTest);
            info.AppendFormat("\"{0}\",", this.TimeSyncTest);
            info.AppendFormat("\"{0}\",", this.ErrorMessage);
            #endregion
            #region Hardware Info
            info.AppendFormat("\"{0}\",", this.Manufacturer);
            info.AppendFormat("\"{0}\",", this.SerialNumber);
            info.AppendFormat("\"{0}\",", this.CDriveFreeMB);
            info.AppendFormat("\"{0}\",", this.CDriveTotalMB);
            info.AppendFormat("\"{0}\",", this.CPUCount);
            info.AppendFormat("\"{0}\",", this.LogicalCPUCount);
            info.AppendFormat("\"{0}\",", this.FreeMemoryMB);
            info.AppendFormat("\"{0}\",", this.TotalMemoryMB);
            #endregion
            #region System Info
            info.AppendFormat("\"{0}\",", this.IPAddress);
            info.AppendFormat("\"{0}\",", this.IPAddresses);
            info.AppendFormat("\"{0}\",", this.TcpConnections);
            info.AppendFormat("\"{0}\",", this.ProcessCount);
            info.AppendFormat("\"{0}\",", this.TotalHandles);
            info.AppendFormat("\"{0}\",", this.TotalThreads);
            info.AppendFormat("\"{0}\",", this.TotalLogons);
            #endregion
            #region Dates and times
            info.AppendFormat("\"{0}\",", this.DCTimeUTC.ToString("yyyy-MM-dd HH:mm"));
            info.AppendFormat("\"{0}\",", this.DST);
            info.AppendFormat("\"{0}\",", this.OSInstallDate.ToString("yyyy-MM-dd HH:mm"));
            info.AppendFormat("\"{0}\",", this.StartupDateTimeUTC.ToString("yyyy-MM-dd HH:mm"));
            info.AppendFormat("\"{0}\",", this.TimeDiffFriendly);
            #endregion
            #region DNS Info
            info.AppendFormat("\"{0}\",", this.DNSResponse);
            info.AppendFormat("\"{0}\",", this.DNSListenAddresses);
            info.AppendFormat("\"{0}\",", this.DNSPublishAddresses);
            info.AppendFormat("\"{0}\",", this.DNSQueryReceived);
            info.AppendFormat("\"{0}\",", this.DNSResponseSent);
            info.AppendFormat("\"{0}\",", this.DNSRecursiveQueries);
            info.AppendFormat("\"{0}\",", this.DNSScavengingInterval);
            #endregion
            #region AD Info
            info.AppendFormat("\"{0}\",", this.DCFlags);
            info.AppendFormat("\"{0}\",", this.HighestCommittedUSN);
            info.AppendFormat("\"{0}\",", this.IsGlobalCatalog);
            info.AppendFormat("\"{0}\",", this.IsInfrastructureManager);
            info.AppendFormat("\"{0}\",", this.IsISTG);
            info.AppendFormat("\"{0}\",", this.IsPDCEmulator);
            info.AppendFormat("\"{0}\",", this.IsRIDManager);
            info.AppendFormat("\"{0}\",", this.NeighborCount);
            info.AppendFormat("\"{0}\",", this.NTDSDitSizeMB);
            info.AppendFormat("\"{0}\",", this.PendingReplicationCount);
            info.AppendFormat("\"{0}\",", this.RepInMB);
            info.AppendFormat("\"{0}\",", this.RepOutMB);
            info.AppendFormat("\"{0}\",", this.SysvolMigrationState);
            info.AppendFormat("\"{0}\"", this.SysvolReplicationType);
            #endregion

            return info.ToString();
        }

        [DebuggerStepThroughAttribute]
        public override string ToString() {
            var info = new StringBuilder();

            info.Append("SystemInfo: ");
            if ((this.DomainController != null) && (this.DomainController.DNSName != null)) {
                info.AppendFormat("DC: {0}; ", this.DomainController.DNSName);
            }
            info.AppendFormat("Check Date: {0}", this.CheckDateTimeUTC.LongDateFriendly());

            return info.ToString();
        }

        public IEnumerable<ValidationResult> Validate(ValidationContext validationContext) {
            if (this.Id == Guid.Empty) {
                yield return new ValidationResult(
                    "Id is required.", new[] { "Id" });
            }

            if (this.DetachedState != DomainModel.DetachedState.Deleted) {
                if (this.CheckDateTimeUTC == DateTime.MinValue) {
                    yield return new ValidationResult(
                        "CheckDateTimeUTC is required.", new[] { "CheckDateTimeUTC" });
                }

                if (this.DomainControllerId == Guid.Empty) {
                    if ((this.DomainController == null) || (this.DomainController.DetachedState != DomainModel.DetachedState.Added)) {
                        yield return new ValidationResult(
                            "DomainControllerId is required.", new[] { "DomainControllerId" });
                    }
                }

                if ((this.HealthCheckSummary != null) && (this.HealthCheckSummary.Id != this.HealthCheckSummaryId)) {
                    yield return new ValidationResult(
                        string.Format("HealthCheckSummaryId does not match HealthCheckSummary.Id.  HealthCheckSummaryId value: {0}; HealthCheckSummary.Id value: {1}",
                        this.HealthCheckSummaryId.ToString(), this.HealthCheckSummary.Id.ToString()),
                        new[] { "HealthCheckSummary, HealthCheckSummaryId" });
                }

                if ((this.DomainController != null) && (this.DomainController.Id != this.DomainControllerId)) {
                    yield return new ValidationResult(
                        "DomainController.Id does not match DomainControllerId.", new[] { "DomainController, DomainControllerId" });
                }

                if (this.PingTest == TestResult.NotPerformed) {
                    yield return new ValidationResult(
                        "PingTest is invalid.  Must be Success or Fail.", new[] { "PingTest" });
                }

                if (this.IPAddress == null) {
                    yield return new ValidationResult(
                        "IPAddress is required.", new[] { "IPAddress" });
                }
                else if (this.IPAddress.Length > IPAddressMaxLength) {
                    yield return new ValidationResult(string.Format("IPAddress length: {0} exceeds max length: {1}",
                        this.IPAddress.Length, IPAddressMaxLength), new[] { "IPAddress" });
                }

                if (this.IPAddresses == null) {
                    yield return new ValidationResult(
                        "IPAddresses is required.", new[] { "IPAddresses" });
                }
                else if (this.IPAddresses.Length > IPAddressesMaxLength) {
                    yield return new ValidationResult(string.Format("IPAddresses length: {0} exceeds max length: {1}",
                        this.IPAddresses.Length, IPAddressesMaxLength), new[] { "IPAddresses" });
                }

                if (this.Manufacturer == null) {
                    yield return new ValidationResult(
                        "Manufacturer is required.", new[] { "Manufacturer" });
                }
                else if (this.Manufacturer.Length > ManufacturerMaxLength) {
                    yield return new ValidationResult(string.Format("Manufacturer length: {0} exceeds max length: {1}",
                        this.Manufacturer.Length, ManufacturerMaxLength), new[] { "Manufacturer" });
                }

                if (this.Model == null) {
                    yield return new ValidationResult(
                        "Model is required.", new[] { "Model" });
                }
                else if (this.Model.Length > ModelMaxLength) {
                    yield return new ValidationResult(string.Format("Model length: {0} exceeds max length: {1}",
                        this.Model.Length, ModelMaxLength), new[] { "Model" });
                }

                if (this.SerialNumber == null) {
                    yield return new ValidationResult(
                        "SerialNumber is required.", new[] { "SerialNumber" });
                }
                else if (this.SerialNumber.Length > SerialNumberMaxLength) {
                    yield return new ValidationResult(string.Format("SerialNumber length: {0} exceeds max length: {1}",
                        this.SerialNumber.Length, SerialNumberMaxLength), new[] { "SerialNumber" });
                }

                if (this.TimeDiff == null) {
                    yield return new ValidationResult(
                        "TimeDiff is required.", new[] { "TimeDiff" });
                }
                else if (this.TimeDiff.Length > TimeDiffMaxLength) {
                    yield return new ValidationResult(string.Format("TimeDiff length: {0} exceeds max length: {1}",
                        this.TimeDiff.Length, TimeDiffMaxLength), new[] { "TimeDiff" });
                }

                if (this.DNSResponse == null) {
                    yield return new ValidationResult(
                        "DNSResponse is required.", new[] { "DNSResponse" });
                }
                else if (this.DNSResponse.Length > DNSResponseMaxLength) {
                    yield return new ValidationResult(string.Format("DNSResponse length: {0} exceeds max length: {1}",
                        this.DNSResponse.Length, DNSResponseMaxLength), new[] { "DNSResponse" });
                }

                if (this.DNSListenAddresses == null) {
                    yield return new ValidationResult(
                        "DNSListenAddresses is required.", new[] { "DNSListenAddresses" });
                }
                else if (this.DNSListenAddresses.Length > DNSListenAddressesMaxLength) {
                    yield return new ValidationResult(string.Format("DNSListenAddresses length: {0} exceeds max length: {1}",
                        this.DNSListenAddresses.Length, DNSListenAddressesMaxLength), new[] { "DNSListenAddresses" });
                }

                if (this.DNSPublishAddresses == null) {
                    yield return new ValidationResult(
                        "DNSPublishAddresses is required.", new[] { "DNSPublishAddresses" });
                }
                else if (this.DNSPublishAddresses.Length > DNSPublishAddressesMaxLength) {
                    yield return new ValidationResult(string.Format("DNSPublishAddresses length: {0} exceeds max length: {1}",
                        this.DNSListenAddresses.Length, DNSPublishAddressesMaxLength), new[] { "DNSPublishAddresses" });
                }
            } // if (this.DetachedState != DomainModel.State.Deleted)
        }  
        #endregion

    }
}
